#pragma once
#include <iostream>
#include <memory>
#include "Common.hpp"
#include "InetAddr.hpp"

const int gbacklog = 16;


class Socket{
public:
	virtual void SocketOrDie() = 0;
	virtual void BindOrDie(uint16_t port) = 0;
	virtual void ListenOrDie(int backlog = gbacklog) = 0;
	virtual int Accept(InetAddr* client) = 0;
	virtual void Close() = 0;
	virtual int Recv(std::string* out) = 0;
	virtual int Send(const std::string& message) = 0;
	virtual int Connect(const std::string& server_ip, uint16_t port) = 0;
	virtual const int GetFd() const = 0;

	// 模板方法
	void BulidTcpSocketMethod(uint16_t port, int backlog = gbacklog){
		SocketOrDie();
		BindOrDie(port);
		ListenOrDie(backlog);
	}

	void BuildTcpClientSocketMethod(){
		SocketOrDie();
	}
	
};

class TcpSocket: public Socket{
public:

	TcpSocket() : _sockfd(-1) {}
	TcpSocket(int sockfd) : _sockfd(sockfd) {}

	// 创建套接字
	void SocketOrDie() override{
		int n = socket(AF_INET, SOCK_STREAM, 0);
		if(n < 0){
			LOG(LogLevel::FATAL) << "socket error";
			exit(SOCKET_ERR);
		}
		LOG(LogLevel::INFO)<< "socket success";
		_sockfd = n;
	}

	// 绑定套接字
	void BindOrDie(uint16_t port) override{
		InetAddr peer(port);
		socklen_t len = peer.AddrLen();
		int n = bind(_sockfd, peer.GetAddrPtr(), len);
		if(n < 0){
			LOG(LogLevel::FATAL) << "bind error";
			exit(BIND_ERR);
		}
		LOG(LogLevel::INFO)<< "bind success";

	}

	// 监听设置
	void ListenOrDie(int backlog = gbacklog) override{
		int n = listen(_sockfd, backlog);
		if(n < 0){
			LOG(LogLevel::FATAL) << "listen error";
			exit(LISTEN_ERR);
		}
		LOG(LogLevel::INFO)<< "listen success";
	}

	int Accept(InetAddr* client) override{
		sockaddr_in peer;
		socklen_t len = sizeof(peer);
		int n = accept(_sockfd, CHAGNGE_PTR(peer), &len);
		if(n < 0){
			LOG(LogLevel::WARNING) << "accept error";
			return -1;
		}
		LOG(LogLevel::INFO)<< "accept success";
		// 设置client
		client->SetAddr(peer);

		// 返回新的套接字
		return n;
		
	}

	void Close() override{
		if(_sockfd >= 0){
			close(_sockfd);
		}
	}

	int Recv(std::string* out) override{
		char buffer[1024];
		int n = recv(_sockfd, buffer, sizeof(buffer), 0);
		if(n > 0){
			buffer[n] = 0;
			*out += std::string(buffer);
			return n;
		}
		else if(n == 0){
			LOG(LogLevel::ERROR)<<"client closed";
			return 0;
		}
		else{
			LOG(LogLevel::ERROR)<<"recv error";
			return -1;
		}
	}

	int Send(const std::string& message){
		int n = send(_sockfd, message.c_str(), message.size(), 0);
		if(n > 0){
			LOG(LogLevel::INFO) << "send success";
		}
		else{
			LOG(LogLevel::ERROR) << "send error";
		}

		return n;
	}

	int Connect(const std::string& server_ip, uint16_t port) override{
		InetAddr server_addr(server_ip, port);
		int n = connect(_sockfd, server_addr.GetAddrPtr(), server_addr.AddrLen());
		if(n < 0){
			LOG(LogLevel::FATAL) << "connect error";
			exit(CONNECT_ERR);
		}

		LOG(LogLevel::INFO) << "connect success";
		return 0;
	}

	const int GetFd() const {
		return _sockfd;
	}

private:
	int _sockfd;
};

